package com.autonavi.yunda.yunji.core.engine.script;

import com.autonavi.yunda.yunji.common.exception.AmapException;
import com.autonavi.yunda.yunji.common.exception.AmapExceptionCode;
import com.autonavi.yunda.yunji.core.engine.scriptresolver.ScriptResolver;
import com.autonavi.yunda.yunji.core.enums.FunctionStatus;
import com.autonavi.yunda.yunji.core.enums.FunctionType;
import com.autonavi.yunda.yunji.core.service.IFunctionConfigService;
import com.autonavi.yunda.yunji.core.vo.EngineFunctionConfigVO;
import com.autonavi.yunda.yunji.core.vo.node.BaseNode;
import com.autonavi.yunda.yunji.core.vo.node.GroovyFunctionNode;
import com.autonavi.yunda.yunji.core.vo.node.JsFunctionNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.autonavi.yunda.yunji.core.config.EngineConstant.FUNCTION_VERSION_SEPARATOR;

/**
 * @author cizhong.wcs
 * @date 2023/7/27 19:16
 * <p>
 * 函数管理脚本管理器
 */
@Slf4j
@Component
public class FunctionScriptClassManager {
    public static Map<String, IFunctionScript> functionScriptClassMap = new ConcurrentHashMap<>();
    @Resource
    private IFunctionConfigService functionConfigService;

    public static IFunctionScript getFunctionScript(String name, String version) {
        IFunctionScript functionScript = functionScriptClassMap.get(name + FUNCTION_VERSION_SEPARATOR + version);
        if (functionScript == null) {
            throw new AmapException(AmapExceptionCode.COMMON_ILLEGAL_PARAM, "函数未发布：" + name + "-" + version);
        }
        return functionScript;
    }


    public void checkAndRefreshFunctionScriptManager() {
        //更新缓存中已有的uri
        functionScriptClassMap.forEach((key, cacheScriptClass) -> {
            String functionName = key.split(FUNCTION_VERSION_SEPARATOR)[0];
            String functionVersion = key.split(FUNCTION_VERSION_SEPARATOR)[1];
            EngineFunctionConfigVO functionConfigVO = functionConfigService.findEngineFunctionConfigVO(functionName, functionVersion);
            boolean dbCacheNotConsistent = !functionConfigVO.getUpdateTime().equals(cacheScriptClass.getUpdateTime());
            if (dbCacheNotConsistent) {
                log.info("dbCacheNotConsistent function need refresh, name : {}, version : {}", functionName, functionVersion);
                try {
                    refreshFunctionCacheByNameVersion(functionName, functionVersion);
                } catch (Exception e) {
                    log.error("checkAndRefreshScriptClassManager error , name : {}, version : {}", functionName, functionVersion, e);
                }
            }
        });
        //更新缓存中没有的函数
        List<Pair<String, String>> existsFunction = functionScriptClassMap.keySet().stream()
                .map(functionKey -> Pair.of(functionKey.split(FUNCTION_VERSION_SEPARATOR)[0], functionKey.split(FUNCTION_VERSION_SEPARATOR)[1]))
                .collect(Collectors.toList());
        List<Pair<String, String>> newPublishedFunction = functionConfigService.listNewPublishedFunction(existsFunction);
        log.warn("新增 function list = {}", newPublishedFunction.stream().map(Pair::toString).collect(Collectors.toList()));
        newPublishedFunction.forEach(pair -> {
            String functionName = pair.getLeft();
            String functionVersion = pair.getRight();
            try {
                refreshFunctionCacheByNameVersion(functionName, functionVersion);
            } catch (Exception e) {
                log.error("checkAndRefreshScriptClassManager error  functionName = {}, functionVersion = {}", functionName, functionVersion, e);
            }
        });
    }


    public IFunctionScript generateFunctionScriptClass(EngineFunctionConfigVO vo) {
        BaseNode node;
        if (vo.getType().equals(FunctionType.groovy)) {
            node = GroovyFunctionNode.from(vo);
        } else {
            node = JsFunctionNode.from(vo);
        }
        return ScriptResolver.resolveNode(node);
    }


    /**
     * 强制刷新函数的缓存
     */
    public void refreshFunctionCacheByNameVersion(String name, String version) {
        EngineFunctionConfigVO vo = functionConfigService.findEngineFunctionConfigVO(name, version);
        if (vo == null || !vo.getStatus().equals(FunctionStatus.PUBLISHED)) {
            functionScriptClassMap.remove(vo.getFunctionKey());
            return;
        }
        IFunctionScript functionScript = generateFunctionScriptClass(vo);
        functionScript.setUpdateTime(vo.getUpdateTime());
        functionScriptClassMap.put(vo.getFunctionKey(), functionScript);
    }

}
